<template>
  <h4>{{ title }}</h4>
  <canvas :id="key" class="c2d" width="1000" height="500"></canvas>
</template>

<script setup>
import { ref, onMounted } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

const key = ref("c2d" + Math.floor(Math.random() * 10000));
const props = defineProps({
  title: String,
});

onMounted(() => {
  const canvas = document.querySelector(`#${key.value}`);

  const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });

  // 1. 创建一个场景
  const scene = new THREE.Scene();

  function createPath(pointsArr) {
    // 将参数数组转换成点数组的形式
    pointsArr = pointsArr.map((point) => new THREE.Vector3(...point));
    // 自定义三维路径 curvePath
    const path = new THREE.CurvePath();
    for (let i = 0; i < pointsArr.length - 1; i++) {
      // 每两个点之间形成一条三维直线
      const lineCurve = new THREE.LineCurve3(pointsArr[i], pointsArr[i + 1]);
      // curvePath有一个curves属性，里面存放组成该三维路径的各个子路径
      path.curves.push(lineCurve);
    }
    return path;
  }

  const pointsArr = [
    [42, 0, 10],
    [21, 0, 10],
    [21, 0, 1],
    [-3, 0, 1],
    [-3, 0, -18],
    [-10, 0, -18],
    [-10, 0, 5],
    [1, 0, 5],
    [1, 0, 24],
    [-27, 0, 24],
    [-27, 0, 18],
    [-46, 0, 19],
    [-46, 0, -4],
    [-25, 0, -6],
    [-25, 0, -19],
    [-35, 0, -20],
    [-35, 0, -26],
    [-30, 0, -30],
    [3, 0, -30],
  ];

  const curve = createPath(pointsArr);

  // 2. 创建管道体
  const tubeGeometry = new THREE.TubeGeometry(curve, 1000, 0.8, 10, false);
  // 纹理贴图：一定要使用透明背景的图片，否则贴图会全部叠在一起，看不出来效果
  const texLoader = new THREE.TextureLoader();
  const texture = texLoader.load(
    "src/assets/img/pipeline.png"
    // "http://n.sinaimg.cn/sinacn17/276/w742h1134/20180416/a986-fytnfyp5975269.jpg"
  );
  // 允许横纵设置矩阵（人话就是可以平铺）
  // 设置阵列模式 RepeatWrapping
  texture.wrapS = THREE.RepeatWrapping;
  texture.wrapT = THREE.RepeatWrapping;
  // 设置x方向的重复数(沿着管道路径方向)
  // 设置y方向的重复数(环绕管道方向)
  texture.repeat.x = 80;
  texture.repeat.y = 1;
  // 设置管道纹理偏移数,便于对中
  texture.offset.y = 0.5;

  // 3. 创建管道材质
  const tubeMaterial = new THREE.MeshPhongMaterial({
    map: texture, // 颜色贴图
    transparent: true,
    color: 0xfcd3d3,
    side: THREE.DoubleSide,
  });

  // 底部网格（可以不设置）
  const gridHelper = new THREE.GridHelper(300, 25, 0x004444, 0x004444);
  scene.add(gridHelper);

  const mesh = new THREE.Mesh(tubeGeometry, tubeMaterial);
  mesh.position.y = 35;
  mesh.position.x = 0;
  mesh.rotateZ(3.14);
  mesh.scale.set(2, 2, 2);
  // 4. 把几何体（管道）和 材质 生成的网格物体添加到场景中
  scene.add(mesh);

  // 5. 设置点光源（关键的一步，没有这一步，没法照亮材质）
  const pointLight = new THREE.PointLight(0xffffff, 1);
  // 点光源位置
  pointLight.position.set(100, 100, 100);
  scene.add(pointLight);

  // 6. 点光源辅助对象（可有可无，辅助观察光源位置，需要时放开 add 方法）
  const sphereSize = 10;
  const pointLightHelper = new THREE.PointLightHelper(pointLight, sphereSize);
  // scene.add(pointLightHelper);

  // 7. 环境光（整个场景的光，添加后整个管道都能更明亮）
  const light = new THREE.AmbientLight(0xffffff, 1);
  scene.add(light);

  // 8. 相机位置（拍照片，关键，Three的每一个帧需要拍下来才能显示）
  const camera = new THREE.PerspectiveCamera(30, 2, 0.1, 2000);
  // 超出视锥体的远裁截面的范围会被剪掉
  camera.position.set(200, 200, 50);

  // 相机指向的场景中心
  camera.lookAt(0, 0, 0);

  // 控制相机
  const controls = new OrbitControls(camera, canvas);
  controls.update();

  // 9. 渲染方法
  renderer.setSize(1000, 500);
  // 用相机(camera)渲染一个场景(scene)。类比相机的拍照动作
  renderer.render(scene, camera);

  // 10. 循环
  function render() {
    // 周期性渲染，更新 canvas 画布的内容
    renderer.render(scene, camera);
    requestAnimationFrame(render);
    // 关键一步，循环改变贴图的位置，css的属性
    texture.offset.x -= 0.04;
  }
  render();

  // 11. 这一步是优化，在窗口改变时，canvas 位置也跟着改变
  // window.onresize = function () {
  //   renderer.setSize(window.innerWidth, window.innerHeight);
  //   // 相机宽高比
  //   camera.aspect = window.innerWidth / window.innerHeight;
  //   // 更新摄像机投影矩阵
  //   camera.updateProjectionMatrix();
  // };
});
</script>

<style scoped>
.read-the-docs {
  color: #888;
}
</style>
